package com.lpf.db_pools;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Vector;

/**
 * 功能描述：真正的连接池管理对象
 */
public class MyPoolsImpl implements IMyPool {

    // 准备好连接池所需要的参数
    private static String jdbcDriver;
    private static String jdbcUrl;
    private static String userName;
    private static String password;
    private static int initCount;
    private static int stepSize;
    private static int poolMaxSize;

    // 初始化好的连接数据库的官道 拿和取得时候 搞一个线程同步的数据结构
    private static Vector<PooledConnection> poolConnects = new Vector<PooledConnection>();

    public MyPoolsImpl() {
        init();
    }

    /**
     * 初始化这些信息的方法
     */
    private void init() {
        try {
            // 流的形式
            InputStream inputStream = MyPoolsImpl.class.getClassLoader().getResourceAsStream("jdbcPool.properties");

            // 管道内的字节数据：key-value形式
            Properties properties = new Properties();
            properties.load(inputStream);

            jdbcDriver = properties.getProperty("jdbcDriver");
            jdbcUrl = properties.getProperty("jdbcUrl");
            userName = properties.getProperty("userName");
            password = properties.getProperty("password");
            initCount = Integer.valueOf(properties.getProperty("initCount"));
            stepSize = Integer.valueOf(properties.getProperty("stepSize"));
            poolMaxSize = Integer.valueOf(properties.getProperty("poolMaxSize"));

            // 创建数据链 通过反射  mysql  Oracle
            Driver driver = (Driver) Class.forName(jdbcDriver).newInstance();
            // 驱动 注册到大管家
            DriverManager.registerDriver(driver);

            //创建数据连接
            createConnections(initCount);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * server
     * 任务获取管道方法（消费连接池里面的管道）
     */
    @Override
    public PooledConnection getConnection() {

        if (poolConnects.size() <= 0) {
            System.out.println("连接池内没有数据库管道对象！");
            // 通常会打印loger.error("连接池内没有数据库管道对象！")
            throw new RuntimeException("连接池内没有数据库管道对象！");
        }

        // 进来获取连接管道对象的时候 真实有效的连接管道  判断检测
        PooledConnection connection = getRealConnection();
        while (connection == null) {
            // 证明里没有空闲的了 扩容
            createConnections(stepSize);
            getRealConnection();
            // 等一下
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }

    /**
     * 拿一个真实有效的连接管道  判断是否被占用    检测 是否是有效的管道
     */
    private synchronized PooledConnection getRealConnection() {

        // 判断连接池里面的管道对象是否是空闲
        for (PooledConnection conn : poolConnects) {
            // 不是繁忙
            if (!conn.isBusy()) {
                // 这里是拿到自定义管道里面的真实管道
                Connection connection = conn.getConn();
                try {
                    // isValid底层源码是去数据查一遍  然后是否有返回 如果有返回代表这个连接管道是有效的 2000
                    if (!connection.isValid(2000)) {
                        // 这里面创建一个有效管道
                        Connection validConn = DriverManager.getConnection(jdbcUrl, userName, password);
                        // connection =validConn
                        conn.setConn(validConn);
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn.setBusy(true);
                return conn;
            }
        }
        return null;
    }

    /**
     * 创建数据连接管道 是 交给连接池去控制管理
     */
    @Override
    public void createConnections(int count) {

        if (count > 0 && poolConnects.size() + count > poolMaxSize) {
            System.out.println("创建连接池管道对象失败，原因是将超过上限值！");
            throw new RuntimeException("创建连接池管道对象失败，原因是将超过上限值！");
        }

        for (int i = 0; i < count; i++) {
            try {
                Connection conn = DriverManager.getConnection(jdbcUrl, userName, password);
                // 将创建出来的物理管道封装到自定义换到里面 并且标示为没有被占用
                PooledConnection pooledConne = new PooledConnection(conn, false);
                // 创建好管道后丢入
                poolConnects.add(pooledConne);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}
